/**
 * @des interface 的使用
 */
// 对方法的约束
interface fn {
  (a: number): number;
}
let person: fn = function(num: number): number {
  return num;
};
// 对对象的约束
interface obj {
  name: string;
  age?: number;
  [aaa: string]: any;
  alert(): any;
}
const o: obj = {
  name: 'htl',
  age: 28,
  alert: () => {}
};

// 对类的约束（和抽象类有点相似，注意区分）
interface Animal {
  name: string;
  eat(): void;
}
class Dog implements Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat() {}
}

// 接口的扩展，此时Person不仅有say，还会有Animal的属性和方法
interface Person extends Animal {
  say(): void;
}

/**
 * @des abstract的使用
 */
// abstract 定义类的标准（通常在基类），抽象类抽象标准。不能直接实例化抽象类
abstract class Ani {
  abstract eat(): any;
}
class Cat extends Ani {
  eat() {}
}

/**
 * @des 泛型
 */

// 方法中使用泛型，以及调用方法 ---泛型方法
function getData<T>(val: T): T {
  return val;
}
getData<number>(1);

// 泛型在类中使用 --- 泛型类
class One<T> {
  constructor(val: T) {
    this.val = val;
  }
  val: T;
}
const one1 = new One<string>('hehe'); // 实例化泛型类时，指定T 的类型为string
const one2 = new One<number>(11); // 实例化泛型类时，指定T 的类型为number

// 类，可以作为参数格式的校验。如同了interface接口的格式校验功能，但是这种情况多数是在和泛型类中配合使用。
class Two {
  name: string;
  age: number;
  constructor(info: { name: string; age: number }) {
    this.name = info.name;
    this.age = info.age;
  }
}
const two: Two = new Two({ name: 'htl', age: 18 }); // 这里const two的时候，就用了Two类作为校验

class MongoDB<T> {
  // 泛型类
  add(info: T) {
    console.log(`添加到数据库的信息有：${info}`);
  }
}
const db = new MongoDB<Two>(); // 这里也用了Two这个类，作为T类型的指定
db.add(two);

// 泛型接口,有两种方式定义。使用也时稍有区别
interface FXInterface<T> {
  // 第一种定义
  (val: T): void;
}
interface FXInterface2 {
  // 第二种定义
  <T>(val: T): void;
}

const f1: FXInterface<string> = <T>(val: T): void => {};
const f2: FXInterface2 = <T>(val: T): void => {};

/**
 * @des 命名空间 namespace，防止命名冲突。当然也可以把整个命名空间以模块的形式暴露出去，如：export A。外部用import引入，就可以使用A空间里面的属性方法了。
 */
// 命名空间里面的属性方法，需要被外部使用时，要用exprot。如f2,就可以通过A.f2 来获取。f1就不行了。
namespace A {
  const f1 = 1;
  export const f2 = 2; //用export暴露出去，于外部使用
}
console.log(A.f2);

/**
 * @des 装饰器，主要与类配合使用，装饰类本身，类的属性，类的方法，类的参数：类装饰器、属性装饰器、方法装饰器、参数装饰器。装饰器分两种，普通装饰器和装饰器工厂，区别为前者无法接受参数。
 * 普通装饰器 写法，function(target) {}; target 就是要装饰的内容。
 * 装饰器工厂 写法：
 * @param param 为装饰器接受到的参数
 * @param target 为要装饰的内容
 * function(params) {
 *    return function(target) {}
 * }
 * 注意：以下例子均以装饰器工厂的写法
 *
 * 装饰器执行顺序：属性 > 方法 > 方法参数 > 类。如果存在多个相同类型装饰器（如多个类装饰器），则从后到前执行。
 */

// 类装饰器写法
// param 接收到的参数
// target 为要装饰的类，如Test
function classDec(param: any) {
  return function(target: any) {};
}
// demo:
@classDec('我是一个类装饰器，装饰的成员为Test')
class Test {
  name: string | undefined;
  age: number | undefined;
}

// 属性装饰器写法
// param 接收到的参数
// target 对于静态成员来说就是类的构造函数，对于实例成员是类的原型对象，如:Test2.prototype
// attr 成员名字,如：age
function propertyDec(param: any) {
  return function(target: any, attr: any) {};
}
// demo:
class Test2 {
  name: string | undefined;
  @propertyDec('我是一个属性装饰器，装饰的成员是age')
  age: number | undefined;
}

// 方法装饰器写法
// param 接收到的参数
// target 对于静态成员来说就是类的构造函数，对于实例成员是类的原型对象，如:Test3.prototype
// methodName 成员名字,如：getData
// desc 成员的描述符
function fnDec(param: any) {
  return function(target: any, methodName: any, desc: any) {};
}
// demo:
class Test3 {
  name: string | undefined;
  age: number | undefined;
  @fnDec('我是方法装饰器，装饰成员为getData')
  getData() {}
}

// 方法参数装饰器写法
// param 接收到的参数
// target 对于静态成员来说就是类的构造函数，对于实例成员是类的原型对象，如:Test4.prototype
// methodName 成员所对应的函数名字,如：getData
// paramIndex 成员在函数参数列表中的索引位置，如：这里成员为id，其位置就是0
function paramDec(param: any) {
  return function(target: any, methodName: any, paramIndex: any) {};
}
// demo:
class Test4 {
  name: string | undefined;
  age: number | undefined;
  getData(@paramDec('我是一个方法参数装饰器') id: any) {}
}
